use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use chrono::{Local};
use serde::{Deserialize, Serialize};
use yew::{Callback, Reducible};
use yew_hooks::{UseMapHandle, UseSetHandle, UseWebSocketHandle};
use crate::db::pojo::pojo::ImMessage;
use crate::business::websocket::local_seq::LocalSeq;
use crate::business::rpc::router::ping_router::do_ping;
use crate::business::websocket::send_data_router::{RpcRequest, RpcResponse};
use crate::business::websocket::dto::ping_dto::PingReq;
use crate::business::websocket::dto::rpc_data::RpcData;
use crate::business::websocket::dto::user_dto::LoginResp;

#[derive(Clone)]
pub struct ImSendData {
    use_web_socket_handle: UseWebSocketHandle,  // 发送消息
    pub(crate) show_message_callback: Rc<RefCell<Option<Callback<ImMessage>>>>,  // 接受消息就掉用此方法
    send_data_set: UseSetHandle<String>, // 发送消息的local_seq列表
    receive_data_set: UseSetHandle<String>, // 接受消息的local_seq列表
    local_seq: Rc<RefCell<Option<LocalSeq>>>,  // local_seq 生成器
    session: UseMapHandle<String, String>, // session 的HashMap
    send_callback: Rc<RefCell<HashMap::<String, Callback<RpcResponse>>>>, // 消息发送列表
    tmp_seq: Rc<RefCell<u64>>,
}

/// websocket发送消息事件
pub enum ImSendDataMsg {
    Request(RpcRequest, Callback<RpcResponse>),  // 发送消息事件
    ShowMessageCallback(Callback<ImMessage>),    // 消息回显事件
    RemoveMessageCallback,                       // 去掉消息回显事件
    SendData(String),                            // 发送回执信息
    LoginSuccess(RpcData<LoginResp>),            // 登录成功事件
    Ping(PingReq),                               //
}

impl PartialEq for ImSendData {
    fn eq(&self, _other: &Self) -> bool {
        true
    }
}

impl ImSendData {
    pub fn new(use_web_socket_handle: UseWebSocketHandle,
               send_data_set: UseSetHandle<String>,
               receive_data_set: UseSetHandle<String>,
               session: UseMapHandle<String, String>)->Self{
        Self{
            use_web_socket_handle,
            show_message_callback: Rc::new(RefCell::new(None)),
            send_data_set,
            receive_data_set,
            local_seq: Rc::new(RefCell::new(None)),
            session,
            send_callback: Rc::new(RefCell::new(HashMap::new())),
            tmp_seq: Rc::new(RefCell::new(0)),
        }
    }
}

impl Reducible for ImSendData {
    type Action = ImSendDataMsg;

    fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
        match action {
            ImSendDataMsg::Request(message, cb) => {
                log::info!("sending message000: ");

                message.send_data(&self, cb);
            },
            ImSendDataMsg::ShowMessageCallback(callback) => {
                log::info!("show message callback");
                // let mut smc = self.show_message_callback.borrow_mut();
                // smc.replace(callback);

                log::info!("show message callback, 22222");

                return Self{
                    use_web_socket_handle: self.use_web_socket_handle.clone(),
                    show_message_callback: Rc::new(RefCell::new(Some(callback))),
                    send_data_set: self.send_data_set.clone(),
                    receive_data_set: self.receive_data_set.clone(),
                    local_seq: self.local_seq.clone(),
                    session: self.session.clone(),
                    send_callback: self.send_callback.clone(),
                    tmp_seq: self.tmp_seq.clone(),
                }.into()
            },
            ImSendDataMsg::RemoveMessageCallback => {
                log::info!("null message callback");
                // let mut mut_call =self.show_message_callback.borrow_mut();
                // * mut_call = None;
                return Self{
                    use_web_socket_handle: self.use_web_socket_handle.clone(),
                    show_message_callback: Rc::new(RefCell::new(None)),
                    send_data_set: self.send_data_set.clone(),
                    receive_data_set: self.receive_data_set.clone(),
                    local_seq: self.local_seq.clone(),
                    session: self.session.clone(),
                    send_callback: self.send_callback.clone(),
                    tmp_seq: self.tmp_seq.clone(),
                }.into()
            },
            ImSendDataMsg::SendData(data) => {
                log::info!("sending data333, {}", data);
                let t1 = self.use_web_socket_handle.clone().ws.clone();
                let websocket = t1.borrow_mut().clone().unwrap();
                websocket.send_with_str(&data).expect("TODO: panic message");
                log::info!("sending data, end");
            },
            ImSendDataMsg:: LoginSuccess(rpc_data)=> {
                let local_seq = rpc_data.local_seq();
                let login_result = rpc_data.data.unwrap();

                if let Ok(login_resp) = login_result {
                    log::info!("set local seq, {:?}, {}", login_resp, local_seq);
                    let mut smc = self.local_seq.borrow_mut();
                    smc.replace(LocalSeq::new(&login_resp.success_data_time));

                    let mut callback_map = self.send_callback.borrow_mut();
                    log::info!("callback map2: {:?}", callback_map);
                    let callback = callback_map.remove(&local_seq);
                    log::info!("set local seq get callback result, {:?}", callback.is_some());
                    if let Some(cb) = callback {
                        log::info!("set local seq emit callback");
                        cb.emit(RpcResponse::LoginResponse(Ok(login_resp)));
                    }
                }
            },
            ImSendDataMsg::Ping(ping_req) => {
                ping(ping_req, &self);
            }
        }
        self
    }
}

impl ImSendData {
    pub fn send_data<T: Serialize + for<'de> Deserialize<'de> + 'static>(&self, path: String, data: T, cb: Callback<RpcResponse>) {
        let mut rpc_data = RpcData::new(path, data, self.increase_local_seq());
        let local_seq = rpc_data.local_seq();

        let session = self.session.current();
        let auth_code = session.get("authCode");
        if let Some(auth_code) = auth_code {
            rpc_data.ext_data_map.insert("authCode".to_owned(), auth_code.to_owned());
        }

        cache_send_data_local_seq(local_seq.clone(), self.send_data_set.clone());

        let json: String = rpc_data.into();
        log::info!("websocket request: {}", json);

        send_data_to_server(&json, self.use_web_socket_handle.clone());

        let mut callback_map = self.send_callback.borrow_mut();
        callback_map.insert(local_seq.clone(), cb);
        log::info!("callback map: {:?}", callback_map);
    }

    fn increase_local_seq(&self)->String {
        if let Some(mut cb) = self.local_seq.borrow_mut().take() {
            cb.increase().to_string()
        } else {
            format!("{}_{}", Local::now().timestamp(), self.increase_tmp_seq())
        }
    }

    fn increase_tmp_seq(&self)->u64 {
        let mut mut_seq = self.tmp_seq.borrow_mut();
        *mut_seq += 1;
        mut_seq.deref().clone()
    }
}

fn send_data_to_server(json_str: &String, use_web_socket_handle: UseWebSocketHandle) {
    let t1 = use_web_socket_handle.ws.clone();
    if let Some(websocket) = t1.borrow_mut().as_ref() {
        websocket.send_with_str(json_str).expect("TODO: panic message");
    }
}

fn ping(ping_req: PingReq, send_data: &ImSendData) {
    log::info!("sending ping 0");
    let response = do_ping(ping_req);
    let local_seq = send_data.local_seq.borrow();
    log::info!("sending ping 1, {:?}, {}", send_data.session.current(), local_seq.is_some());
    let session = send_data.session.current();
    let auth_code = session.get("authCode");
    if let Some(mut local_seq) = local_seq.clone() {
        log::info!("sending ping 2");
        let mut rpc_data = RpcData::new("pingToServer".to_string(), response, local_seq.increase().to_string());
        rpc_data.ext_data_map.insert("authCode".to_owned(), auth_code.unwrap().to_string());

        let local_seq = rpc_data.local_seq().clone();
        cache_send_data_local_seq(local_seq, send_data.send_data_set.clone());

        let response_json = rpc_data.into();
        log::info!("sending ping, {}", response_json);
        send_data_to_server(&response_json, send_data.use_web_socket_handle.clone());
    }
}

fn cache_send_data_local_seq(local_seq: String, send_data_set: UseSetHandle<String>) {
    let send_data_set = send_data_set.clone();
    send_data_set.insert(local_seq);
    log::info!("send data set size: {}", send_data_set.current().len());
}